﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace MyCompanyName.AbpZeroTemplate.Prm.Mpp
{

    public class MppHelper
    {
        /// <summary>
        /// 导出
        /// </summary>
        /// <param name="info">要导出的数据实体</param>
        /// <param name="templatPath">mpp空模板文件流</param>
        /// <param name="savaPath">要输出的mpp文件流</param>
        public static void Export(MppInfo info,
            Stream templatPath,
            Stream savaPath)
        {
            List<Aspose.Tasks.Task> tasks = new List<Aspose.Tasks.Task>(info.Tasks.Count);
            foreach (var item in info.Tasks)
            {
                tasks.Add(new Aspose.Tasks.Task()
                {
                    GUID = item.Id.ToString(),
                    Name = item.Name,
                    Start = item.StartDate,
                    Finish = item.EndDate,
                    PercentComplete = 0,
                    DurationFormat = Aspose.Tasks.TimeUnitType.Day,
                    Duration = item.Duration,
                    IsMilestone = item.IsMilestone,
                });
            }


            Aspose.Tasks.Project project = new Aspose.Tasks.Project(templatPath);
            project.WorkFormat = Aspose.Tasks.TimeUnitType.Day;


            var root = info.Tasks.Where(x => x.ParentId == Guid.Empty);
            foreach (var item in root)
            {
                var tsk = tasks.First(x => x.GUID == item.Id.ToString());
                if (info.Tasks.Any(x => x.ParentId == item.Id))
                {
                    var subIds = info.Tasks
                        .Where(x => x.ParentId == item.Id)
                        .Select(x => Convert.ToString(x.Id));

                    var subs = tasks.Where(x => subIds.Contains(x.GUID));
                    tsk.Children.AddRange(subs);
                }
                project.RootTask.Children.Add(tsk);
            }

            Recalculate(project);

            foreach (var item in info.Dependencies)
            {
                Aspose.Tasks.Task task1 = tasks.First(x => x.GUID == item.FromTaskId.ToString());
                Aspose.Tasks.Task task2 = tasks.First(x => x.GUID == item.ToTaskId.ToString());

                Aspose.Tasks.TaskLinkType tlt = Aspose.Tasks.TaskLinkType.FinishToStart;
                switch (item.LinkType)
                {
                    case TaskDependencyType.FF:
                        tlt = Aspose.Tasks.TaskLinkType.FinishToFinish;
                        break;
                    case TaskDependencyType.FS:
                        tlt = Aspose.Tasks.TaskLinkType.FinishToStart;
                        break;
                    case TaskDependencyType.SF:
                        tlt = Aspose.Tasks.TaskLinkType.StartToFinish;
                        break;
                    case TaskDependencyType.SS:
                        tlt = Aspose.Tasks.TaskLinkType.StartToStart;
                        break;
                    default:
                        break;
                }
                var lt = new Aspose.Tasks.TaskLink(task1, task2, tlt);
                project.TaskLinks.Add(lt);
            }

            foreach (var item in info.Resources)
            {
                var resource = new Aspose.Tasks.Resource()
                {
                    Uid = item.Id,
                    Name = item.Name
                };
                project.Resources.Add(resource);
            }


            foreach (var item in info.Assignments)
            {
                Aspose.Tasks.Task task = tasks.First(x => x.GUID == item.TaskId.ToString());
                Aspose.Tasks.Resource resource = project.Resources.First(x => x.Uid == item.ResourceId);
                project.AddResourceAssignment(task, resource, (double)item.PercentUnits / 100);
            }

            foreach (var item in tasks)
            {
                item.PercentComplete = info.Tasks
                        .Where(x => x.Id.ToString() == item.GUID)
                        .Select(x => x.PercentDone)
                        .First();
            }

            //project.Name = info.ProjectName;
            //project.Author = "Author";
            //project.LastAuthor = "LastAuthor";
            //project.Revision = info.Revision;
            //project.Keywords = "Keywords";
            //project.Comments = "Comments";
            //project.Company = "Company";

            Recalculate(project);
            project.Save(savaPath, Aspose.Tasks.Saving.SaveFileFormat.MPP);

        }

        private static void Recalculate(Aspose.Tasks.Project project)
        {
            project.CalcTaskIds();
            project.CalcTaskUids();
            project.CalcResourceIds();
            project.CalcResourceUids();
            project.CalcResourceAssignmentIds();
            project.CalcResourceAssignmentUids();
            project.CalcCalendarUids();
            project.UpdateReferences();
        }


        /// <summary>
        /// 导入
        /// </summary>
        /// <param name="mppStream">上传导入的mpp文件流</param>
        /// <returns></returns>
        public static MppInfo Import(Stream mppStream)
        {
            var res = new MppInfo();

            Aspose.Tasks.Project project = new Aspose.Tasks.Project(mppStream);
            res.Revision = project.Revision;
            res.ProjectName = project.Name;

            var allTasks = project.RootTask.Children;
            foreach (var Tsk in allTasks)
            {
                getSubs(Tsk, res);
            }


            var allResources = project.Resources;
            foreach (var item in allResources)
            {
                res.Resources.Add(new Mpp.ResourceInfo()
                {
                    Id = item.Uid,
                    Name = item.Name
                });
            }

            var allLinks = project.TaskLinks;
            foreach (var item in allLinks)
            {
                res.Dependencies.Add(new Mpp.DependencyInfo()
                {
                    FromTaskId = new Guid(item.PredTask.GUID),
                    ToTaskId = new Guid(item.SuccTask.GUID),
                    LinkType = ToDependencyType(item.LinkType)
                });
            }

            var ras = project.ResourceAssignments;
            foreach (var item in ras)
            {
                res.Assignments.Add(new Mpp.AssignmentInfo()
                {
                    ResourceId = item.Resource.Uid,
                    TaskId = new Guid(item.Task.GUID),
                    PercentUnits = Convert.ToInt32(Math.Round(item.Units * 100))
                });
            }
            return res;
        }

        private static Mpp.TaskDependencyType ToDependencyType(Aspose.Tasks.TaskLinkType s)
        {
            switch (s)
            {
                case Aspose.Tasks.TaskLinkType.FinishToFinish:
                    return TaskDependencyType.FF;
                case Aspose.Tasks.TaskLinkType.FinishToStart:
                    return TaskDependencyType.FS;
                case Aspose.Tasks.TaskLinkType.StartToFinish:
                    return TaskDependencyType.SF;
                case Aspose.Tasks.TaskLinkType.StartToStart:
                    return TaskDependencyType.SS;
                default:
                    break;
            }
            throw new Exception("未知的TaskLinkType ： " + s);
        }

        private static void getSubs(Aspose.Tasks.Task Tsk, Mpp.MppInfo res)
        {
            var resources = Tsk.ParentProject.GetResourceAssignmentsByTask(Tsk);

            Guid _parentId = Guid.Empty;
            if (Tsk.ParentTask != Tsk.ParentProject.RootTask)
            {
                _parentId = new Guid(Tsk.ParentTask.GUID);
            }

            Mpp.TaskInfo newTask = new TaskInfo()
            {
                StartDate = Tsk.Start,
                EndDate = Tsk.Finish,
                Duration = Tsk.Duration,

                PercentDone = Tsk.PercentComplete,
                IsMilestone = Tsk.IsMilestone,

                Name = Tsk.Name,
                Id = new Guid(Tsk.GUID),
                ParentId = _parentId,
                Resources = new Dictionary<int, double>(resources.Count)
            };

            if (resources.Any())
            {
                foreach (var item in resources)
                {
                    if (item.Resource != null)
                    {
                        newTask.Resources.Add(item.Resource.Uid, item.Units);
                    }
                }
            }

            res.Tasks.Add(newTask);

            if (Tsk.Children.Any())
            {
                foreach (var item in Tsk.Children)
                {
                    getSubs(item, res);
                }
            }

        }

    }

    #region 实体 定义

    public class MppInfo
    {
        public MppInfo()
        {
            Resources = new List<ResourceInfo>();
            Tasks = new List<TaskInfo>();
            Dependencies = new List<DependencyInfo>();
            Assignments = new List<AssignmentInfo>();
            Revision = 1;
        }
        public List<ResourceInfo> Resources { get; set; }

        public List<TaskInfo> Tasks { get; set; }

        public List<DependencyInfo> Dependencies { get; set; }

        public List<AssignmentInfo> Assignments { get; set; }

        public int Revision { get; set; }
        public string ProjectName { get; set; }
    }

    public class TaskInfo
    {
        public Guid ParentId { get; set; }

        public Guid Id { get; set; }

        public string Name { get; set; }

        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }

        //public string DurationUnit { get; set; }
        //public decimal Duration { get; set; }

        public int PercentDone { get; set; }

        //public DateTime BaselineStartDate { get; set; }
        //public DateTime BaselineEndDate { get; set; }
        //public int BaselinePercentDone { get; set; }   

        public Dictionary<int, double> Resources { get; set; }

        public TimeSpan Duration { get; set; }

        public bool IsMilestone { get; set; }
    }

    public class ResourceInfo
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class AssignmentInfo
    {
        public int ResourceId { get; set; }
        public Guid TaskId { get; set; }

        /// <summary>
        /// 1~100,default=100
        /// </summary>
        public int PercentUnits { get; set; }
    }

    public class DependencyInfo
    {
        public Guid FromTaskId { get; set; }
        public Guid ToTaskId { get; set; }
        public TaskDependencyType LinkType { get; set; }
    }

    public enum TaskDependencyType
    {
        FF = 0,
        FS = 1,
        SF = 2,
        SS = 3,
    }

    #endregion
}
